﻿using Lypshare.Net.Server.Service;
using System.Timers;
using System.Net;
using System;

namespace Lypshare.Net.Server
{
	/// <summary>
	/// Client.
	/// </summary>
	/// <exception cref='ArgumentNullException'>
	/// Is thrown when the argument null exception.
	/// </exception>
	/// <exception cref='InvalidOperationException'>
	/// Is thrown when the invalid operation exception.
	/// </exception>
    public class Client
    {
        // --------------------------------------------------------
        // Class Events
        // --------------------------------------------------------


        /// <summary>
        /// Occurs when disconnected.
        /// </summary>
        public event Action<Client> Disconnected = null;


        // --------------------------------------------------------
        // Class Variables
        // --------------------------------------------------------


        /// <summary>
        /// The _id.
        /// </summary>
        private int _id = 0;


        /// <summary>
        /// The _authed.
        /// </summary>
        private bool _authed = false;


        /// <summary>
        /// The _scope.
        /// </summary>
        private Scope _scope = null;
		
		
		/// <summary>
        /// The _timer.
        /// </summary>
        private Timer _timer = null;
		
		
		/// <summary>
		/// The _address.
		/// </summary>
		private IPAddress _address = null;


        /// <summary>
        /// The _callback.
        /// </summary>
        private IAccessServiceCallback _callback = null;


        // --------------------------------------------------------
        // Class Properties
        // --------------------------------------------------------


        /// <summary>
        /// Gets the identifier.
        /// </summary>
        /// <value>
        /// The identifier.
        /// </value>
        public int Id
        {
            get { return _id; }

        } // Property: Id


        /// <summary>
        /// Gets a value indicating whether this instance is authed.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is authed; otherwise, <c>false</c>.
        /// </value>
        public bool IsAuthed
        {
            get { return _authed; }

        } // Property: IsAuthed
		
		
		/// <summary>
		/// Gets a value indicating whether this instance has scope.
		/// </summary>
		/// <value>
		/// <c>true</c> if this instance has scope; otherwise, <c>false</c>.
		/// </value>
		public bool HasScope
        {
            get { return _scope != null; }

        } // Property: HasScope


        /// <summary>
        /// Gets the scope.
        /// </summary>
        /// <value>
        /// The scope.
        /// </value>
        public Scope Scope
        {
            get { return _scope; }

        } // Property: Scope
		
		
		/// <summary>
		/// Gets the address.
		/// </summary>
		/// <value>
		/// The address.
		/// </value>
		public IPAddress Address
		{
			get { return _address; }
			
		} // Property: Address


        /// <summary>
        /// Gets the callback.
        /// </summary>
        /// <value>
        /// The callback.
        /// </value>
        public IAccessServiceCallback Callback
        {
            get { return _callback; }

        } // Property: Callback


        // --------------------------------------------------------
        // Class Functions
        // --------------------------------------------------------


        /// <summary>
        /// Initializes a new instance of the <see cref="Lypshare.Net.Server.Client"/> class.
        /// </summary>
        /// <param name='id'>
        /// Identifier.
        /// </param>
        /// <param name='address'>
        /// Address.
        /// </param>
        /// <param name='callback'>
        /// Callback.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public Client(int id, IPAddress address, IAccessServiceCallback callback)
        {
			if(address == null) throw new ArgumentNullException("address");
            if (callback == null) throw new ArgumentNullException("callback");

            _id = id;
			_address = address;
            _callback = callback;

            if (!ClientManager.HasValidator)
                _authed = true;
			
			_timer = new Timer(10000);
			
            // After the timer elapsed,
            // the client will be disconnect
            _timer.Elapsed += (s, e) =>
                Disconnect();

            _timer.Start();

        } // Constructor


        /// <summary>
        /// Auth the specified username and password.
        /// </summary>
        /// <param name='username'>
        /// If set to <c>true</c> username.
        /// </param>
        /// <param name='password'>
        /// If set to <c>true</c> password.
        /// </param>
        public bool Auth(String username, String password)
        {
            // Auth the session
            _authed = ClientManager.Auth(
                username, password);

            return _authed;

        } // Auth


        /// <summary>
        /// Join the specified scope.
        /// </summary>
        /// <param name='scope'>
        /// Scope.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when the argument null exception.
        /// </exception>
        /// <exception cref='InvalidOperationException'>
        /// Is thrown when the invalid operation exception.
        /// </exception>
        public void Join(Scope scope)
        {
            if (scope == null)
                throw new ArgumentNullException("scope");

            if (!IsAuthed) throw new InvalidOperationException(
                 "Client is not authed.");

            // Delete client from old scope
            if (HasScope) Scope.Remove(this);

            _scope = scope;

            // Add client to scope
            _scope.Add(this);

        } // Join


        /// <summary>
        /// Synchronize the specified client and port.
        /// </summary>
        /// <param name='client'>
        /// Client.
        /// </param>
        /// <param name='port'>
        /// Port.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        /// <exception cref='InvalidOperationException'>
        /// Is thrown when an operation cannot be performed.
        /// </exception>
        public int Synchronize(Client client, int port)
        {
			if(client == null)
				throw new ArgumentNullException("client");
			
            if (!IsAuthed) throw new InvalidOperationException(
                 "Client is not authed.");

            return _scope.Synchronize(
                this, client, port);

        } // Synchronize


        /// <summary>
        /// Keeps the connection.
        /// </summary>
        public void KeepConnection()
        {
            _timer.Stop();
            _timer.Start();

        } // KeepConnection


        /// <summary>
        /// Disconnect this instance.
        /// </summary>
        public void Disconnect()
        {
            // Stop dispose timer
            _timer.Stop();

            if (HasScope)
                _scope.Remove(this);

            // Delete callback
            _callback = null;

            // Raise event
            OnDisconnected();

        } // Dispose


        // --------------------------------------------------------
        // Class Handles
        // --------------------------------------------------------


        /// <summary>
        /// Raises the disconnected event.
        /// </summary>
        protected virtual void OnDisconnected()
        {
            if (Disconnected != null)
                Disconnected(this);

        } // OnDisconencted

    } // class Client
} // namespace Lypshare.Net.Server